對應 30天挑戰精通 PowerShell 該書第 20 章。
看完這章後,我開始對既有運行的 script 有些想法,有種原來還能這樣用的想法,特別是驗證以及處理錯誤這塊,不過讓我們就先跟著書中的章節走下去,之後有機會再來分享。
書中提供了一個初始化例子,是來自前幾天的例子( 透過 Get-CimInstance
cmdlet 獲取指定計算機上的邏輯磁碟資訊,並篩選出硬碟驅動器。它將結果依據設備ID排序,並以表格格式顯示每個磁碟的設備ID、可用空間 (以 MB 為單位)、總大小 (以 GB 為單位) 以及可用空間 ),然而這邊將原先的 Format-Table
改成用 Select-Object
,因為 Format-Table
用於將輸出直接格式化為表格形式,方便在控制台中查看。雖然這對於立即顯示很有用,但在指令碼或函式中使用並非最佳實踐,特別是當程式碼需要重複使用或進一步處理時。
Select-Object
Select-Object 會建立包含選定屬性的物件,允許進一步的操作或管線處理。
輸出仍然是結構化的資料,可以在管線中傳遞、匯出或進一步操作。
param (
$computername = 'localhost',
$drivetype = 3
)
Get-CimInstance -class Win32_LogicalDisk -computername $computername `
-Filter "drivetype=$drivetype" |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID ,
@{Label = 'FreeSpace(MB)'; expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
在指令碼中添加 [CmdletBinding()]
屬性,可以將其轉變為進階指令碼( advanced script ),使其功能和使用體驗更接近 PowerShell 的內建 cmdlet,從而提高指令碼的專業性和靈活性。
添加 [CmdletBinding()]
後,指令碼自動支援 PowerShell 的通用參數,例如 -Verbose
、 -Debug
和 -ErrorAction
等,這些參數是 cmdlet 的標準特徵之一,能顯著增強指令碼的控制力和調試性。
[CmdletBinding()]
param (
[string]$computername = 'localhost',
[int]$drivetype = 3
)
try {
Get-CimInstance -Class Win32_LogicalDisk -ComputerName $computername -Filter "drivetype=$drivetype" -ErrorAction Stop -ErrorVariable GetCimError |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID,
@{Label = 'FreeSpace(MB)'; Expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; Expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; Expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
}
catch {
Write-Error "Failed to retrieve logical disk information for $computername. Details: $GetCimError"
}
-ErrorAction Stop
:確保在遇到錯誤時即刻中止操作,以便進行更詳細的錯誤處理。-ErrorVariable
:將錯誤訊息儲存到指定變數 ( GetCimError
) 中,這樣可以在 catch
區塊中顯示更詳細的錯誤資訊,有助於調試和排除故障。這個在稍後的 20.3 讓參數成為強制性的章節裡,會使用到。
使用 [CmdletBinding()]
可以讓指令碼支援更豐富的參數屬性,如 Mandatory
、 Position
和 ValueFromPipeline
,這些屬性讓參數的管理和使用更加靈活與直觀。例如:
Mandatory
:使參數成為必填項,避免因用戶輸入不完整而出現錯誤。Position
:讓參數具備順序,使輸入變得簡化,提升用戶體驗。ValueFromPipeline
:允許從管道中接收參數,方便與其他 cmdlet 組合使用。透過 [Parameter(Mandatory=$True)]
的設定,可以將參數標記為強制性,確保使用者在執行指令碼或函式時必須提供該參數的值。如果未提供該參數,PowerShell 會自動提示使用者輸入所需的值,從而避免因缺少必要參數而導致的執行錯誤。
[CmdletBinding()]
param (
[Parameter(Mandatory=$True, Position=0)]
[string]$computername,
[Parameter(Position=1)]
[int]$drivetype = 3
)
Get-CimInstance -Class Win32_LogicalDisk -ComputerName $computername `
-Filter "drivetype=$drivetype" |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID,
@{Label = 'FreeSpace(MB)'; Expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; Expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; Expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
在這段範例中, [Parameter(Mandatory=$True)]
的設定使 $computername
成為一個必填參數。當使用者執行這段指令碼時,必須提供 -computername
的值,否則 PowerShell 會自動提示要求使用者輸入一個值。
Mandatory=$True
:這個屬性確保使用者在執行指令碼或函式時必須提供該參數,否則會顯示提示,防止因為缺少必要參數而導致的執行錯誤。Position=0
:這個屬性允許使用者不必明確指定參數名,而是直接通過位置提供參數值,這使指令碼更加簡潔和易用。當未提供 -computername
參數時,PowerShell 會自動提示用戶輸入該值。請看截圖:
這樣,PowerShell 通過互動提示確保使用者提供必要的參數,從而避免指令碼中因缺少參數而引發的執行錯誤。這種設計方式不僅提升了指令碼的可用性,還使得使用者更容易理解該參數的重要性。
使用 [Alias('')]
可以為目標參數新增替代名稱,也就是 **別名 **。別名的好處在於為參數提供替代名稱,使指令碼更加靈活,並且對使用者更加友好。這在使用者習慣了其他工具的命名方式,或者根據自身使用經驗期望不同的參數名稱時特別有用。
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[Alias('host', 'server')]
[string]$computername,
[Parameter(Position=1)]
[int]$drivetype = 3
)
Get-CimInstance -Class Win32_LogicalDisk -ComputerName $computername `
-Filter "drivetype=$drivetype" |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID,
@{Label = 'FreeSpace(MB)'; Expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; Expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; Expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
在範例中,為參數 $computername
添加了多個別名 [Alias('host', 'server')]
。這些別名允許使用者在呼叫指令碼時使用不同的名稱來指向同一個參數,因此可以使用以下方式執行 script
.\chapter20_baseline.ps1 -host 'localhost'
.\chapter20_baseline.ps1 -server 'localhost'
透過 [ValidateSet()]
可以指定某個參數只接受特定範圍內的值,這樣可以確保指令碼只處理有效的資料,並且在使用者提供無效值時立即向其回饋錯誤訊息。這對於避免錯誤輸入並提高指令碼的穩健性和可預測性非常有用。
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[Alias('host')]
[string]$computername,
[Parameter(Position=1)]
[ValidateSet(2, 3)]
[int]$drivetype = 3
)
Get-CimInstance -Class Win32_LogicalDisk -ComputerName $computername `
-Filter "drivetype=$drivetype" |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID,
@{Label = 'FreeSpace(MB)'; Expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; Expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; Expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
在範例中, [ValidateSet(2, 3)]
用於參數 $drivetype
,這表示該參數只接受值 2
或 3
。當使用者提供其他任何值時,PowerShell 會立刻顯示錯誤並提示有效的選擇。例如:
這種立即回饋的方式有效避免了不必要的錯誤執行,確保腳本只處理合理的輸入。
除了 [ValidateSet()]
,PowerShell 還提供了其他一些有用的驗證屬性,這些屬性可以根據需求為參數的值提供不同形式的驗證,增加指令碼的可靠性。
[ValidateRange()]
$drivesize
的值在 1
到 10
之間,可以使用:[Parameter(Position=2)]
[ValidateRange(1, 10)]
[int]$drivesize
這樣,當使用者提供超出 1-10
的值時,PowerShell 會提示錯誤。
這樣,當使用者提供超出 1-10
的值時,PowerShell 會提示錯誤。
[ValidatePattern()]
$username
必須符合以字母開頭並且只能包含字母或數字的格式,可以使用:[Parameter(Position=3)]
[ValidatePattern('^[a-zA-Z][a-zA-Z0-9]*$')]
[string]$username
這樣,如果使用者提供不符合格式的值,例如包含特殊字符的名稱,PowerShell 會提示錯誤。
Write-Verbose
允許輸出詳細的操作訊息,這些訊息可以幫助使用者了解指令碼的內部運作。這些詳細信息只有在使用 -Verbose
參數時才會顯示,因此它不會影響指令碼的正常運行,但能在需要時提供有價值的診斷信息,從而提升指令碼的親和力和使用者體驗。
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[Alias('host')]
[string]$computername,
[Parameter(Position=1)]
[ValidateSet(2, 3)]
[int]$drivetype = 3
)
Write-Verbose "Connecting to $computername"
Write-Verbose "Looking for drive type $drivetype"
# 執行主要命令
Get-CimInstance -Class Win32_LogicalDisk -ComputerName $computername `
-Filter "drivetype=$drivetype" |
Sort-Object -Property DeviceID |
Select-Object -Property DeviceID,
@{Label = 'FreeSpace(MB)'; Expression = { $_.FreeSpace / 1MB -as [int] } },
@{Label = 'Size(GB)'; Expression = { $_.Size / 1GB -as [int] } },
@{Label = '%Free'; Expression = { $_.FreeSpace / $_.Size * 100 -as [int] } }
Write-Verbose "Finished running command"
在這段範例中, Write-Verbose
用於輸出多個關鍵的執行步驟信息,例如:
這些詳細輸出可以在運行指令碼時使用 -Verbose
參數啟用,例如:
.\chapter20_baseline.ps1 -computername 'localhost' -Verbose
啟用 -Verbose
後,使用者會看到額外的執行訊息,這些訊息有助於了解指令碼的每一步動作,特別是在排查問題或需要了解指令碼執行流程時非常有幫助。
Write-Verbose
允許你在指令碼中增加更多的訊息來解釋指令碼目前正在做什麼,這樣使用者就可以更加清楚每一步的操作。例如,在執行時間較長的指令碼中,Verbose 信息可以讓使用者了解當前執行的狀態,減少焦慮感。-Verbose
可以顯示更多的背景信息,便於理解腳本在不同步驟的執行情況,這有助於快速找到可能的問題點。Write-Verbose
不會干擾指令碼的正常輸出結果,它只在需要時才會顯示額外信息。這使得它比 Write-Host
或其他輸出命令更適合用於診斷和詳細輸出。Day 25 - 使用正規表示式來解析文字檔